/*
 * Tencent is pleased to support the open source community by making IoT Hub
 available.
 * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.

 * Licensed under the MIT License (the "License"); you may not use this file
 except in
 * compliance with the License. You may obtain a copy of the License at
 * http://opensource.org/licenses/MIT

 * Unless required by applicable law or agreed to in writing, software
 distributed under the License is
 * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 KIND,
 * either express or implied. See the License for the specific language
 governing permissions and
 * limitations under the License.
 *
 */

#include "lite-utils.h"
#include "qcloud_iot_export.h"
#include "qcloud_iot_import.h"
#include "utils_timer.h"

/* anis color control codes */
#define ANSI_COLOR_RED    "\x1b[31m"
#define ANSI_COLOR_GREEN  "\x1b[32m"
#define ANSI_COLOR_YELLOW "\x1b[33m"
#define ANSI_COLOR_BLUE   "\x1b[34m"
#define ANSI_COLOR_RESET  "\x1b[0m"

static DeviceInfo sg_devInfo;
static Timer      sg_reportTimer;

static MQTTEventType sg_subscribe_event_result = MQTT_EVENT_UNDEF;
static bool          sg_control_msg_arrived    = false;
static char          sg_data_report_buffer[2048];
size_t               sg_data_report_buffersize = sizeof(sg_data_report_buffer) / sizeof(sg_data_report_buffer[0]);

/*data_config.c can be generated by tools/codegen.py -c xx/product.json*/
/*-----------------data config start  -------------------*/

#define TOTAL_PROPERTY_COUNT 4
#define MAX_STR_NAME_LEN     (64)

static sDataPoint sg_DataTemplate[TOTAL_PROPERTY_COUNT];

typedef enum {
    eCOLOR_RED   = 0,
    eCOLOR_GREEN = 1,
    eCOLOR_BLUE  = 2,
} eColor;

typedef struct _ProductDataDefine {
    TYPE_DEF_TEMPLATE_BOOL   m_light_switch;
    TYPE_DEF_TEMPLATE_ENUM   m_color;
    TYPE_DEF_TEMPLATE_INT    m_brightness;
    TYPE_DEF_TEMPLATE_STRING m_name[MAX_STR_NAME_LEN + 1];
} ProductDataDefine;

static ProductDataDefine sg_ProductData;

static void _init_data_template(void)
{
    memset((void *)&sg_ProductData, 0, sizeof(ProductDataDefine));

    sg_ProductData.m_light_switch         = 0;
    sg_DataTemplate[0].data_property.key  = "power_switch";
    sg_DataTemplate[0].data_property.data = &sg_ProductData.m_light_switch;
    sg_DataTemplate[0].data_property.type = TYPE_TEMPLATE_BOOL;

    sg_ProductData.m_color                = eCOLOR_RED;
    sg_DataTemplate[1].data_property.key  = "color";
    sg_DataTemplate[1].data_property.data = &sg_ProductData.m_color;
    sg_DataTemplate[1].data_property.type = TYPE_TEMPLATE_ENUM;

    sg_ProductData.m_brightness           = 0;
    sg_DataTemplate[2].data_property.key  = "brightness";
    sg_DataTemplate[2].data_property.data = &sg_ProductData.m_brightness;
    sg_DataTemplate[2].data_property.type = TYPE_TEMPLATE_INT;

    strncpy(sg_ProductData.m_name, sg_devInfo.device_name, MAX_STR_NAME_LEN);
    sg_ProductData.m_name[strlen(sg_devInfo.device_name)] = '\0';
    sg_DataTemplate[3].data_property.key                  = "name";
    sg_DataTemplate[3].data_property.data                 = sg_ProductData.m_name;
    sg_DataTemplate[3].data_property.data_buff_len        = MAX_STR_NAME_LEN;
    sg_DataTemplate[3].data_property.type                 = TYPE_TEMPLATE_STRING;
};
/*-----------------data config end  -------------------*/

/*event_config.c can be generated by tools/codegen.py -c xx/product.json*/
/*-----------------event config start  -------------------*/
#ifdef EVENT_POST_ENABLED
#define EVENT_COUNTS              (3)
#define MAX_EVENT_STR_MESSAGE_LEN (64)
#define MAX_EVENT_STR_NAME_LEN    (64)

static TYPE_DEF_TEMPLATE_BOOL   sg_status;
static TYPE_DEF_TEMPLATE_STRING sg_message[MAX_EVENT_STR_MESSAGE_LEN + 1];
static DeviceProperty           g_propertyEvent_status_report[] = {

    {.key = "status", .data = &sg_status, .type = TYPE_TEMPLATE_BOOL},
    {.key = "message", .data = sg_message, .type = TYPE_TEMPLATE_STRING},
};

static TYPE_DEF_TEMPLATE_FLOAT sg_voltage;
static DeviceProperty          g_propertyEvent_low_voltage[] = {

    {.key = "voltage", .data = &sg_voltage, .type = TYPE_TEMPLATE_FLOAT},
};

static TYPE_DEF_TEMPLATE_STRING sg_name[MAX_EVENT_STR_NAME_LEN + 1];
static TYPE_DEF_TEMPLATE_INT    sg_error_code;
static DeviceProperty           g_propertyEvent_hardware_fault[] = {

    {.key = "name", .data = sg_name, .type = TYPE_TEMPLATE_STRING},
    {.key = "error_code", .data = &sg_error_code, .type = TYPE_TEMPLATE_INT},
};

static sEvent g_events[] = {

    {
        .event_name   = "status_report",
        .type         = "info",
        .timestamp    = 0,
        .eventDataNum = sizeof(g_propertyEvent_status_report) / sizeof(g_propertyEvent_status_report[0]),
        .pEventData   = g_propertyEvent_status_report,
    },
    {
        .event_name   = "low_voltage",
        .type         = "alert",
        .timestamp    = 0,
        .eventDataNum = sizeof(g_propertyEvent_low_voltage) / sizeof(g_propertyEvent_low_voltage[0]),
        .pEventData   = g_propertyEvent_low_voltage,
    },
    {
        .event_name   = "hardware_fault",
        .type         = "fault",
        .timestamp    = 0,
        .eventDataNum = sizeof(g_propertyEvent_hardware_fault) / sizeof(g_propertyEvent_hardware_fault[0]),
        .pEventData   = g_propertyEvent_hardware_fault,
    },
};

/*-----------------event config end -------------------*/

static void update_events_timestamp(sEvent *pEvents, int count)
{
    int i;

    for (i = 0; i < count; i++) {
        if (NULL == (&pEvents[i])) {
            Log_e("null event pointer");
            return;
        }
#ifdef EVENT_TIMESTAMP_USED
        pEvents[i].timestamp = HAL_Timer_current_sec();  // should be UTC and
                                                         // accurate
#else
        pEvents[i].timestamp = 0;
#endif
    }
}

static void event_post_cb(void *pClient, MQTTMessage *msg)
{
    Log_d("recv event reply, clear event");
    IOT_Event_clearFlag(pClient, FLAG_EVENT0);
}

// event check and post
static void eventPostCheck(void *client)
{
    int      i;
    int      rc;
    uint32_t eflag;
    uint8_t  EventCont;
    sEvent * pEventList[EVENT_COUNTS];

    eflag = IOT_Event_getFlag(client);
    if ((EVENT_COUNTS > 0) && (eflag > 0)) {
        EventCont = 0;
        for (i = 0; i < EVENT_COUNTS; i++) {
            if ((eflag & (1 << i)) & ALL_EVENTS_MASK) {
                pEventList[EventCont++] = &(g_events[i]);
                update_events_timestamp(&g_events[i], 1);
            }
        }

        rc = IOT_Post_Event(client, sg_data_report_buffer, sg_data_report_buffersize, EventCont, pEventList,
                            event_post_cb);
        if (rc < 0) {
            Log_e("event post failed: %d", rc);
        }
    }
}

#endif

/*action_config.c can be generated by tools/codegen.py -c xx/product.json*/
/*-----------------action config start  -------------------*/
#ifdef ACTION_ENABLED

#define TOTAL_ACTION_COUNTS (1)

static TYPE_DEF_TEMPLATE_INT sg_blink_in_period    = 5;
static DeviceProperty        g_actionInput_blink[] = {
    {.key = "period", .data = &sg_blink_in_period, .type = TYPE_TEMPLATE_INT}};
static TYPE_DEF_TEMPLATE_BOOL sg_blink_out_result    = 0;
static DeviceProperty         g_actionOutput_blink[] = {

    {.key = "result", .data = &sg_blink_out_result, .type = TYPE_TEMPLATE_BOOL},
};

static DeviceAction g_actions[] = {

    {
        .pActionId  = "blink",
        .timestamp  = 0,
        .input_num  = sizeof(g_actionInput_blink) / sizeof(g_actionInput_blink[0]),
        .output_num = sizeof(g_actionOutput_blink) / sizeof(g_actionOutput_blink[0]),
        .pInput     = g_actionInput_blink,
        .pOutput    = g_actionOutput_blink,
    },
};
/*-----------------action config end    -------------------*/
static void OnActionCallback(void *pClient, const char *pClientToken, DeviceAction *pAction)
{
    int        i;
    sReplyPara replyPara;

    // control light blink
    int             period       = 0;
    DeviceProperty *pActionInput = pAction->pInput;
    for (i = 0; i < pAction->input_num; i++) {
        if (!strcmp(pActionInput[i].key, "period")) {
            period = *((int *)pActionInput[i].data);
        } else {
            Log_e("no such input[%s]!", pActionInput[i].key);
        }
    }

    // do blink
    HAL_Printf("%s[lighting blink][****]" ANSI_COLOR_RESET, ANSI_COLOR_RED);
    HAL_SleepMs(period * 1000);
    HAL_Printf("\r%s[lighting blink][****]" ANSI_COLOR_RESET, ANSI_COLOR_GREEN);
    HAL_SleepMs(period * 1000);
    HAL_Printf("\r%s[lighting blink][****]\n" ANSI_COLOR_RESET, ANSI_COLOR_RED);

    // construct output
    memset((char *)&replyPara, 0, sizeof(sReplyPara));
    replyPara.code       = eDEAL_SUCCESS;
    replyPara.timeout_ms = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
    strcpy(replyPara.status_msg,
           "action execute success!");  // add the message about the action resault

    DeviceProperty *pActionOutnput   = pAction->pOutput;
    *(int *)(pActionOutnput[0].data) = 0;  // set result

    IOT_ACTION_REPLY(pClient, pClientToken, sg_data_report_buffer, sg_data_report_buffersize, pAction, &replyPara);
}

static int _register_data_template_action(void *pTemplate_client)
{
    int i, rc;

    for (i = 0; i < TOTAL_ACTION_COUNTS; i++) {
        rc = IOT_Template_Register_Action(pTemplate_client, &g_actions[i], OnActionCallback);
        if (rc != QCLOUD_RET_SUCCESS) {
            rc = IOT_Template_Destroy(pTemplate_client);
            Log_e("register device data template action failed, err: %d", rc);
            return rc;
        } else {
            Log_i("data template action=%s registered.", g_actions[i].pActionId);
        }
    }

    return QCLOUD_RET_SUCCESS;
}
#endif

static void event_handler(void *pclient, void *handle_context, MQTTEventMsg *msg)
{
    uintptr_t packet_id = (uintptr_t)msg->msg;

    switch (msg->event_type) {
        case MQTT_EVENT_UNDEF:
            Log_i("undefined event occur.");
            break;

        case MQTT_EVENT_DISCONNECT:
            Log_i("MQTT disconnect.");
            break;

        case MQTT_EVENT_RECONNECT:
            Log_i("MQTT reconnect.");
            break;

        case MQTT_EVENT_SUBCRIBE_SUCCESS:
            sg_subscribe_event_result = msg->event_type;
            Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
            break;

        case MQTT_EVENT_SUBCRIBE_TIMEOUT:
            sg_subscribe_event_result = msg->event_type;
            Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
            break;

        case MQTT_EVENT_SUBCRIBE_NACK:
            sg_subscribe_event_result = msg->event_type;
            Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
            break;

        case MQTT_EVENT_PUBLISH_SUCCESS:
            Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
            break;

        case MQTT_EVENT_PUBLISH_TIMEOUT:
            Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
            break;

        case MQTT_EVENT_PUBLISH_NACK:
            Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
            break;
        default:
            Log_i("Should NOT arrive here.");
            break;
    }
}

/*add user init code, like sensor init*/
static void _usr_init(void)
{
    Log_d("add your init code here");
}

/*control msg from server will trigger this callback*/
static void OnControlMsgCallback(void *pClient, const char *pJsonValueBuffer, uint32_t valueLength,
                                 DeviceProperty *pProperty)
{
    int i = 0;

    for (i = 0; i < TOTAL_PROPERTY_COUNT; i++) {
        /* handle self defined string/json here. Other properties are dealed in
         * _handle_delta()*/
        if (strcmp(sg_DataTemplate[i].data_property.key, pProperty->key) == 0) {
            sg_DataTemplate[i].state = eCHANGED;
            Log_i("Property=%s changed", pProperty->key);
            sg_control_msg_arrived = true;
            return;
        }
    }

    Log_e("Property=%s changed no match", pProperty->key);
}

static void OnReportReplyCallback(void *pClient, Method method, ReplyAck replyAck, const char *pJsonDocument,
                                  void *pUserdata)
{
    Log_i("recv report_reply(ack=%d): %s", replyAck, pJsonDocument);
}

// register data template properties
static int _register_data_template_property(void *pTemplate_client)
{
    int i, rc;

    for (i = 0; i < TOTAL_PROPERTY_COUNT; i++) {
        rc = IOT_Template_Register_Property(pTemplate_client, &sg_DataTemplate[i].data_property, OnControlMsgCallback);
        if (rc != QCLOUD_RET_SUCCESS) {
            rc = IOT_Template_Destroy(pTemplate_client);
            Log_e("register device data template property failed, err: %d", rc);
            return rc;
        } else {
            Log_i("data template property=%s registered.", sg_DataTemplate[i].data_property.key);
        }
    }

    return QCLOUD_RET_SUCCESS;
}

/*get property state, changed or not*/
static eDataState get_property_state(void *pProperyData)
{
    int i;

    for (i = 0; i < TOTAL_PROPERTY_COUNT; i++) {
        if (sg_DataTemplate[i].data_property.data == pProperyData) {
            return sg_DataTemplate[i].state;
        }
    }

    Log_e("no property matched");
    return eNOCHANGE;
}

/*set property state, changed or no change*/
static void set_propery_state(void *pProperyData, eDataState state)
{
    int i;

    for (i = 0; i < TOTAL_PROPERTY_COUNT; i++) {
        if (sg_DataTemplate[i].data_property.data == pProperyData) {
            sg_DataTemplate[i].state = state;
            break;
        }
    }
}

/* demo for light logic deal */
static void deal_down_stream_user_logic(void *client, ProductDataDefine *light)
{
    int         i;
    const char *ansi_color         = NULL;
    const char *ansi_color_name    = NULL;
    char        brightness_bar[]   = "||||||||||||||||||||";
    int         brightness_bar_len = strlen(brightness_bar);

    /* light color */
    switch (light->m_color) {
        case eCOLOR_RED:
            ansi_color      = ANSI_COLOR_RED;
            ansi_color_name = " RED ";
            break;
        case eCOLOR_GREEN:
            ansi_color      = ANSI_COLOR_GREEN;
            ansi_color_name = "GREEN";
            break;
        case eCOLOR_BLUE:
            ansi_color      = ANSI_COLOR_BLUE;
            ansi_color_name = " BLUE";
            break;
        default:
            ansi_color      = ANSI_COLOR_YELLOW;
            ansi_color_name = "UNKNOWN";
            break;
    }

    /* light brightness bar */
    brightness_bar_len =
        (light->m_brightness >= 100) ? brightness_bar_len : (int)((light->m_brightness * brightness_bar_len) / 100);
    for (i = brightness_bar_len; i < strlen(brightness_bar); i++) {
        brightness_bar[i] = '-';
    }

    if (light->m_light_switch) {
        /* light is on , show with the properties*/
        HAL_Printf("%s[  lighting  ]|[color:%s]|[brightness:%s]|[%s]\n" ANSI_COLOR_RESET, ansi_color, ansi_color_name,
                   brightness_bar, light->m_name);
    } else {
        /* light is off */
        HAL_Printf(ANSI_COLOR_YELLOW "[  light is off ]|[color:%s]|[brightness:%s]|[%s]\n" ANSI_COLOR_RESET,
                   ansi_color_name, brightness_bar, light->m_name);
    }

    if (eCHANGED == get_property_state(&light->m_light_switch)) {
#ifdef EVENT_POST_ENABLED
        if (light->m_light_switch) {
            *(TYPE_DEF_TEMPLATE_BOOL *)g_events[0].pEventData[0].data = 1;
            memset((TYPE_DEF_TEMPLATE_STRING *)g_events[0].pEventData[1].data, 0, MAX_EVENT_STR_MESSAGE_LEN);
            strcpy((TYPE_DEF_TEMPLATE_STRING *)g_events[0].pEventData[1].data, "light on");
        } else {
            *(TYPE_DEF_TEMPLATE_BOOL *)g_events[0].pEventData[0].data = 0;
            memset((TYPE_DEF_TEMPLATE_STRING *)g_events[0].pEventData[1].data, 0, MAX_EVENT_STR_MESSAGE_LEN);
            strcpy((TYPE_DEF_TEMPLATE_STRING *)g_events[0].pEventData[1].data, "light off");
        }

        // switch state changed set EVENT0 flag, the events will be posted by
        // eventPostCheck
        IOT_Event_setFlag(client, FLAG_EVENT0);
#else
        Log_d("light switch state changed");
#endif
    }
}

/*example for cycle report, you can delete this for your needs*/
static void cycle_report(Timer *reportTimer)
{
    int i;

    if (expired(reportTimer)) {
        for (i = 0; i < TOTAL_PROPERTY_COUNT; i++) {
            set_propery_state(sg_DataTemplate[i].data_property.data, eCHANGED);
            countdown_ms(reportTimer, 5000);
        }
    }
}

/*get local property data, like sensor data*/
static void _refresh_local_property(void)
{
    // add your local property refresh logic, cycle report for example
    cycle_report(&sg_reportTimer);
}

static int find_wait_report_property(DeviceProperty *pReportDataList[])
{
    int i, j;

    for (i = 0, j = 0; i < TOTAL_PROPERTY_COUNT; i++) {
        if (eCHANGED == sg_DataTemplate[i].state) {
            pReportDataList[j++]     = &(sg_DataTemplate[i].data_property);
            sg_DataTemplate[i].state = eNOCHANGE;
        }
    }

    return j;
}

/* demo for up-stream code */
static int deal_up_stream_user_logic(DeviceProperty *pReportDataList[], int *pCount)
{
    // refresh local property
    _refresh_local_property();

    /*find propery need report*/
    *pCount = find_wait_report_property(pReportDataList);

    return (*pCount > 0) ? QCLOUD_RET_SUCCESS : QCLOUD_ERR_FAILURE;
}

/*You should get the real info for your device, here just for example*/
static int _get_sys_info(void *handle, char *pJsonDoc, size_t sizeOfBuffer)
{
    /*platform info has at least one of module_hardinfo/module_softinfo/fw_ver
     * property*/
    DeviceProperty plat_info[] = {
        {.key = "module_hardinfo", .type = TYPE_TEMPLATE_STRING, .data = "ESP8266"},
        {.key = "module_softinfo", .type = TYPE_TEMPLATE_STRING, .data = "V1.0"},
        {.key = "fw_ver", .type = TYPE_TEMPLATE_STRING, .data = QCLOUD_IOT_DEVICE_SDK_VERSION},
        {.key = "imei", .type = TYPE_TEMPLATE_STRING, .data = "11-22-33-44"},
        {.key = "lat", .type = TYPE_TEMPLATE_STRING, .data = "22.546015"},
        {.key = "lon", .type = TYPE_TEMPLATE_STRING, .data = "113.941125"},
        {NULL, NULL, 0}  // end
    };

    /*self define info*/
    DeviceProperty self_info[] = {
        {.key = "append_info", .type = TYPE_TEMPLATE_STRING, .data = "your self define info"}, {NULL, NULL, 0}  // end
    };

    return IOT_Template_JSON_ConstructSysInfo(handle, pJsonDoc, sizeOfBuffer, plat_info, self_info);
}

void *sub_dev1_thread(void *user_arg, char *product_id, char *device_name)
{
    DeviceProperty *pReportDataList[TOTAL_PROPERTY_COUNT];
    sReplyPara      replyPara;
    int             ReportCont;
    int             rc;
    void *          pGateWayClient = user_arg;

    Log_d("sub_dev1_thread ...");

    // init connection
    TemplateInitParams init_params = DEFAULT_TEMPLATE_INIT_PARAMS;
    init_params.product_id         = product_id;
    init_params.device_name        = device_name;
    init_params.event_handle.h_fp  = event_handler;

    void *client = IOT_Template_Construct(&init_params, IOT_Gateway_Get_Mqtt_Client(pGateWayClient));
    if (client != NULL) {
        Log_i("Cloud Device Construct Success");
    } else {
        Log_e("Cloud Device Construct Failed");
        return NULL;
    }

#ifdef MULTITHREAD_ENABLED
    /*only gateway yield thread read mqtt packet*/
    int  code;
    bool state = IOT_Gateway_Get_Yield_Status(pGateWayClient, &code);
    IOT_Template_Set_Yield_Status(client, state, code);
#endif

    // usr init
    _usr_init();

    // init data template
    _init_data_template();

    // register data template propertys here
    rc = _register_data_template_property(client);
    if (rc == QCLOUD_RET_SUCCESS) {
        Log_i("Register data template propertys Success");
    } else {
        Log_e("Register data template propertys Failed: %d", rc);
        return NULL;
    }

// register data template actions here
#ifdef ACTION_ENABLED
    rc = _register_data_template_action(client);
    if (rc == QCLOUD_RET_SUCCESS) {
        Log_i("Register data template actions Success");
    } else {
        Log_e("Register data template actions Failed: %d", rc);
        return NULL;
    }
#endif

    HAL_SleepMs(1000);  // wait subcrible ack
    // report device info, then you can manager your product by these info, like
    // position
    rc = _get_sys_info(client, sg_data_report_buffer, sg_data_report_buffersize);
    if (QCLOUD_RET_SUCCESS == rc) {
        rc = IOT_Template_Report_SysInfo_Sync(client, sg_data_report_buffer, sg_data_report_buffersize,
                                              QCLOUD_IOT_MQTT_COMMAND_TIMEOUT);
        if (rc != QCLOUD_RET_SUCCESS) {
            Log_e("Report system info fail, err: %d", rc);
            return NULL;
        }
    } else {
        Log_e("Get system info fail, err: %d", rc);
    }

    // get the property changed during offline
    rc = IOT_Template_GetStatus_sync(client, QCLOUD_IOT_MQTT_COMMAND_TIMEOUT);
    if (rc != QCLOUD_RET_SUCCESS) {
        Log_e("Get data status fail, err: %d", rc);
        return NULL;
    } else {
        Log_d("Get data status success");
    }

    // init a timer for cycle report, you could delete it or not for your needs
    InitTimer(&sg_reportTimer);
    while (IOT_Template_IsConnected(client) || QCLOUD_RET_SUCCESS == rc) {
        rc = IOT_Template_Yield(client, 200);
        if (QCLOUD_RET_SUCCESS != rc) {
            Log_d("Template Yield without mqtt err, rc:%d", rc);
        }

        /* handle control msg from server */
        if (sg_control_msg_arrived) {
            deal_down_stream_user_logic(client, &sg_ProductData);
            /* control msg should reply, otherwise server treat device didn't receive
             * and retain the msg which would be get by  get status*/
            memset((char *)&replyPara, 0, sizeof(sReplyPara));
            replyPara.code          = eDEAL_SUCCESS;
            replyPara.timeout_ms    = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
            replyPara.status_msg[0] = '\0';  // add extra info to replyPara.status_msg when error occured

            rc = IOT_Template_ControlReply(client, sg_data_report_buffer, sg_data_report_buffersize, &replyPara);
            if (rc == QCLOUD_RET_SUCCESS) {
                Log_d("Contol msg reply success");
                sg_control_msg_arrived = false;
            } else {
                Log_e("Contol msg reply failed, err: %d", rc);
            }
        }

        /*report msg to server*/
        /*report the lastest properties's status*/
        if (QCLOUD_RET_SUCCESS == deal_up_stream_user_logic(pReportDataList, &ReportCont)) {
            rc = IOT_Template_JSON_ConstructReportArray(client, sg_data_report_buffer, sg_data_report_buffersize,
                                                        ReportCont, pReportDataList);
            if (rc == QCLOUD_RET_SUCCESS) {
                rc = IOT_Template_Report(client, sg_data_report_buffer, sg_data_report_buffersize,
                                         OnReportReplyCallback, NULL, QCLOUD_IOT_MQTT_COMMAND_TIMEOUT);
                if (rc == QCLOUD_RET_SUCCESS) {
                    Log_i("data template reporte success");
                } else {
                    Log_e("data template reporte failed, err: %d", rc);
                }
            } else {
                Log_e("construct reporte data failed, err: %d", rc);
            }
        }

#ifdef EVENT_POST_ENABLED
        eventPostCheck(client);
#endif
        HAL_SleepMs(1000);
    }

    rc = IOT_Template_Destroy_Except_MQTT(client);
    return NULL;
}
